#include <stdio.h>

#include "osipparser2/osip_md5.h"
#include "osipparser2/osip_port.h"
#include <string.h>

/* TAKEN from rcf2617.txt */

#define HASHLEN 16
typedef char HASH[HASHLEN];

#define HASHHEXLEN 32
typedef char HASHHEX[HASHHEXLEN + 1];

#define IN
#define OUT

void CvtHex(IN HASH Bin, OUT HASHHEX Hex)
{
    unsigned short i;
    unsigned char j;

    for (i = 0; i < HASHLEN; i++)
    {
        j = (Bin[i] >> 4) & 0xf;
        if (j <= 9)
            Hex[i * 2] = (j + '0');
        else
            Hex[i * 2] = (j + 'a' - 10);
        j = Bin[i] & 0xf;
        if (j <= 9)
            Hex[i * 2 + 1] = (j + '0');
        else
            Hex[i * 2 + 1] = (j + 'a' - 10);
    };
    Hex[HASHHEXLEN] = '\0';
}

/* calculate H(A1) as per spec */
static void
DigestCalcHA1(IN const char *pszAlg, IN const char *pszUserName, IN const char *pszRealm, IN const char *pszPassword, IN const char *pszNonce, IN const char *pszCNonce, OUT HASHHEX SessionKey)
{
    osip_MD5_CTX Md5Ctx;
    HASH HA1;
    HASHHEX HA1Hex;

    osip_MD5Init(&Md5Ctx);
    osip_MD5Update(&Md5Ctx, (unsigned char *)pszUserName, (unsigned int)strlen(pszUserName));
    osip_MD5Update(&Md5Ctx, (unsigned char *)":", 1);
    osip_MD5Update(&Md5Ctx, (unsigned char *)pszRealm, (unsigned int)strlen(pszRealm));
    osip_MD5Update(&Md5Ctx, (unsigned char *)":", 1);
    osip_MD5Update(&Md5Ctx, (unsigned char *)pszPassword, (unsigned int)strlen(pszPassword));
    osip_MD5Final((unsigned char *)HA1, &Md5Ctx);
    if ((pszAlg != NULL) &&  (pszAlg, "md5-sess") == 0)
    {
        CvtHex(HA1, HA1Hex);
        osip_MD5Init(&Md5Ctx);
        osip_MD5Update(&Md5Ctx, (unsigned char *)HA1Hex, HASHHEXLEN);
        osip_MD5Update(&Md5Ctx, (unsigned char *)":", 1);
        osip_MD5Update(&Md5Ctx, (unsigned char *)pszNonce, (unsigned int)strlen(pszNonce));
        osip_MD5Update(&Md5Ctx, (unsigned char *)":", 1);
        osip_MD5Update(&Md5Ctx, (unsigned char *)pszCNonce, (unsigned int)strlen(pszCNonce));
        osip_MD5Final((unsigned char *)HA1, &Md5Ctx);
    }
    CvtHex(HA1, SessionKey);
}

/* calculate request-digest/response-digest as per HTTP Digest spec */
static void
DigestCalcResponse(IN HASHHEX HA1,               /* H(A1) */
                   IN const char *pszNonce,      /* nonce from server */
                   IN const char *pszNonceCount, /* 8 hex digits */
                   IN const char *pszCNonce,     /* client nonce */
                   IN const char *pszQop,        /* qop-value: "", "auth", "auth-int" */
                   IN int Aka,                   /* Calculating AKAv1-MD5 response */
                   IN const char *pszMethod,     /* method from the request */
                   IN const char *pszDigestUri,  /* requested URL */
                   IN HASHHEX HEntity,           /* H(entity body) if qop="auth-int" */
                   OUT HASHHEX Response
                   /* request-digest or response-digest */)
{
    osip_MD5_CTX Md5Ctx;
    HASH HA2;
    HASH RespHash;
    HASHHEX HA2Hex;

    /* calculate H(A2) */
    osip_MD5Init(&Md5Ctx);
    osip_MD5Update(&Md5Ctx, (unsigned char *)pszMethod, (unsigned int)strlen(pszMethod));
    osip_MD5Update(&Md5Ctx, (unsigned char *)":", 1);
    osip_MD5Update(&Md5Ctx, (unsigned char *)pszDigestUri, (unsigned int)strlen(pszDigestUri));

    if (pszQop == NULL)
    {
        goto auth_withoutqop;
    }
    else if (0 == osip_strcasecmp(pszQop, "auth-int"))
    {
        goto auth_withauth_int;
    }
    else if (0 == osip_strcasecmp(pszQop, "auth"))
    {
        goto auth_withauth;
    }

auth_withoutqop:
    osip_MD5Final((unsigned char *)HA2, &Md5Ctx);
    CvtHex(HA2, HA2Hex);

    /* calculate response */
    osip_MD5Init(&Md5Ctx);
    osip_MD5Update(&Md5Ctx, (unsigned char *)HA1, HASHHEXLEN);
    osip_MD5Update(&Md5Ctx, (unsigned char *)":", 1);
    osip_MD5Update(&Md5Ctx, (unsigned char *)pszNonce, (unsigned int)strlen(pszNonce));
    osip_MD5Update(&Md5Ctx, (unsigned char *)":", 1);

    goto end;

auth_withauth_int:

    osip_MD5Update(&Md5Ctx, (unsigned char *)":", 1);
    osip_MD5Update(&Md5Ctx, (unsigned char *)HEntity, HASHHEXLEN);

auth_withauth:
    osip_MD5Final((unsigned char *)HA2, &Md5Ctx);
    CvtHex(HA2, HA2Hex);

    /* calculate response */
    osip_MD5Init(&Md5Ctx);
    osip_MD5Update(&Md5Ctx, (unsigned char *)HA1, HASHHEXLEN);
    osip_MD5Update(&Md5Ctx, (unsigned char *)":", 1);
    osip_MD5Update(&Md5Ctx, (unsigned char *)pszNonce, (unsigned int)strlen(pszNonce));
    osip_MD5Update(&Md5Ctx, (unsigned char *)":", 1);
    if (Aka == 0)
    {
        osip_MD5Update(&Md5Ctx, (unsigned char *)pszNonceCount, (unsigned int)strlen(pszNonceCount));
        osip_MD5Update(&Md5Ctx, (unsigned char *)":", 1);
        osip_MD5Update(&Md5Ctx, (unsigned char *)pszCNonce, (unsigned int)strlen(pszCNonce));
        osip_MD5Update(&Md5Ctx, (unsigned char *)":", 1);
        osip_MD5Update(&Md5Ctx, (unsigned char *)pszQop, (unsigned int)strlen(pszQop));
        osip_MD5Update(&Md5Ctx, (unsigned char *)":", 1);
    }
end:
    osip_MD5Update(&Md5Ctx, (unsigned char *)HA2Hex, HASHHEXLEN);
    osip_MD5Final((unsigned char *)RespHash, &Md5Ctx);
    CvtHex(RespHash, Response);
}

/*
REGISTER sip:34020000002000000001@192.168.1.232:5060 SIP/2.0
Via: SIP/2.0/UDP 192.168.1.108:5068;rport;branch=z9hG4bK164237570
Route: <sip:34020000001180000001@192.168.1.232:5060;lr>
From: <sip:34020000001180000001@192.168.1.108:5068>;tag=286511440
To: <sip:34020000001180000001@192.168.1.108:5068>
Call-ID: 2073631518@192.168.1.108
CSeq: 2 REGISTER
Contact: <sip:34020000001180000001@192.168.1.108:5068>
Authorization: Digest username="34020000001180000001", realm="3402000000", nonce="25d55ad283aa400af464c76d713c07ad", uri="sip:34020000002000000001@192.168.1.232:5060", response="1a4285fa51c3af2fdc8755e8a7b0711f", algorithm=MD5, cnonce="0a4f113b", qop=auth, nc=00000001
Max-Forwards: 70
User-Agent: SIP UAS V3.0.0.705103
Expires: 3600
Content-Length: 0
 */

int main(int argn, char **argv)
{

    {
        unsigned char src[] = "123456";
        osip_MD5_CTX Md5Ctx;
        unsigned char HA1[33] = {0};
        osip_MD5Init(&Md5Ctx);
        osip_MD5Update(&Md5Ctx, src, 6);
        osip_MD5Final((unsigned char *)HA1, &Md5Ctx);
        printf("res[2]:");
        for (size_t i = 0; i < 16; i++)
        {
            printf("%02x", HA1[i] & 0xFF);
        }
        printf("\n");
        //	e10adc3949ba59abbe56e057f20f883e
    }

    /*
    void DigestCalcHA1(
    IN char * pszAlg,
    IN char * pszUserName,
    IN char * pszRealm,
    IN char * pszPassword,
    IN char * pszNonce,
    IN char * pszCNonce,
    OUT HASHHEX SessionKey
    )
     */
    char pszAlg[] = "MD5";
    char pszUserName[] = "34020000001180000001";//"41010299111117999086";
    char pszRealm[] = "3402000000";//"4101029911";
    char pszPassword[] = "12345678";//"1234";
    char pszNonce[] = "25d55ad283aa400af464c76d713c07ad";//"1806613454";
    char pszCNonce[] = "0a4f113b";//"";
    // md5(41010299111117999086:4101029911:1234) = a25b9207c02f195bd12e711541cfb832

    /*
    void DigestCalcResponse(
    IN HASHHEX HA1,           /* H(A1) 
    IN char * pszNonce,       /* nonce from server 
    IN char * pszNonceCount,  /* 8 hex digits 
    IN char * pszCNonce,      /* client nonce 
    IN char * pszQop,         /* qop-value: "", "auth", "auth-int" 
    IN char * pszMethod,      /* method from the request 
    IN char * pszDigestUri,   /* requested URL 
    IN HASHHEX HEntity,       /* H(entity body) if qop="auth-int" 
    OUT HASHHEX Response      /* request-digest or response-digest 
    )
     */
    // char pszNonce[] = "1806613454";
    char pszNonceCount[] = "00000001";//"";
    // char pszCNonce[] = "";
    char pszQop[] = "auth";//"";
    char pszMethod[] = "REGISTER";
    char pszDigestUri[] = "sip:34020000002000000001@192.168.1.232:5060";//"sip:41010299111117999086@192.168.1.86:5060";

    // md5(REGISTER:sip:41010299111117999086@192.168.1.86:5060)=7933163119cb2a18105a03326d6f460e

    //md5(a25b9207c02f195bd12e711541cfb832:1806613454:7933163119cb2a18105a03326d6f460e) = 6c090dbcde4a19f4cad94808c1a322e2

    // result
    char pszResponse[] = "1a4285fa51c3af2fdc8755e8a7b0711f";//"6c090dbcde4a19f4cad94808c1a322e2";

    HASHHEX HA1;
    HASHHEX Response;

    DigestCalcHA1(pszAlg, pszUserName, pszRealm, pszPassword, pszNonce, pszCNonce, HA1);
    // printf("HA1=%s\n", HA1);

    DigestCalcResponse(HA1, pszNonce, pszNonceCount, pszCNonce, pszQop, 0, pszMethod, pszDigestUri, NULL, Response);

    printf("pszResponse=%s\n", pszResponse);
    printf("Response=%s\n", Response);

    return 0;
}